/**
 * \file   emitir.cpp
 * \author Fabian Jesus Machado
 * \date   Septiembre de 2011
 * 
 *  Este archivo es parte del proyecto NPASM, un ensamblador para
 * microcontroladores PIC con sintaxis Intel, desarrollado inicialmente
 * por Fabian Machado (fabianjsm@gmail.com, twitter@fabianjsm).
 *  El codigo fuente esta diponible en el repositorio SVN de Google:
 *      http://npasm.googlecode.com/svn/trunk.
 * 
 * Septiembre de 2011.
 * 
 */
#include <iostream>
#include "auxiliar.h"
#include "emitir.h"
#include "entorno.h"
#include "sintaxis.h"
#include "mm.h"
#include "opcode.h"

using namespace std;

template<class T>
void op_arg_incorrecto(T e)
{
	if(!e)
		throw(string("combinacion operando-argumento incorrecta"));
};

/** Esta funcion es amiga de la clase \a entorno */
void aceptar_etiqueta(simbolo *sbl, int ambito)
{
	if(Entorno->GetPaso() != 1)
		return;
	if(sbl->GetTipo() == ETIQUETA) {
		int ln = sbl->GetLindef();
		const char *s = sbl->GetNombre().c_str();
		throw(mkstring("etiqueta '%s' redefinida en linea %d", s, ln));
	} else {
		sbl->SetTipo(ETIQUETA);
		sbl->SetLindef(Entorno->GetLinea());
		sbl->SetVal((unsigned int) Entorno->m->p);
	}
}

/** Esta funcion es amiga de la clase \a entorno */
void emitir_instr(linea_asm *l)
{
	if(!l->i)
		return;
	size_t paso = Entorno->GetPaso();
	if(paso == 1) {
		Entorno->m->p++;
		return;
	}
	opcode_t *op = l->i->GetOp();
	switch(l->t) {
	case INSTR_0:
		// modelo: instr
		Entorno->m->SetROM(op->op0);
		break;
	case INSTR_1A:
		// modelo: instr k
		Entorno->m->SetROM(op->op0 | l->arg0.val.num);
		break;
	case INSTR_1FW:
		if(l->arg0.tipo == ARG_F) {
			// modelo: instr [k]
			Entorno->m->SetROM(op->op0 | l->arg0.val.num);
		} else if(l->arg0.tipo == ARG_W) {
			op_arg_incorrecto(op->op1);
			// modelo: instr k
			Entorno->m->SetROM(op->op1);
		}
		break;
	case INSTR_2FW_FKW:
		if(l->arg0.tipo == ARG_F) {
			op_arg_incorrecto(l->arg1.tipo == ARG_W);
			// modelo: instr [k], w
			Entorno->m->SetROM(op->op0 | (l->arg0.val.num & 0x7F));
		} else {
			op_arg_incorrecto(l->arg1.tipo != ARG_W);
			if(l->arg1.tipo == ARG_F) {
				// modelo: instr w, [k]
				Entorno->m->SetROM(op->op1 | (l->arg1.val.num & 0x7F));
			} else {
				// modelo: instr w, k
				Entorno->m->SetROM(op->op2 | l->arg1.val.num);
			}
		}
		break;
	case INSTR_2FK:
		if(l->arg1.tipo == NO_ARG) {
			op_arg_incorrecto(op->op0);
			// modelo: instr [k]
			Entorno->m->SetROM(op->op0 | (l->arg0.val.num & 0x7F));
		} else {
			unsigned short rom = op->op1;
			rom |= l->arg0.val.num & 0x7F;
			rom |= (l->arg1.val.num & 0x07) << 7;
			// modelo: instr [k].k
			Entorno->m->SetROM(rom);
		}
		break;
	}
}

/** Esta funcion es amiga de la clase \a entorno */
void emitir_mkretlw(unsigned long valor, unsigned long tipo)
{
	int n;
	char c;
	const char *s = &c;
	if(tipo == CADENA) {
		s = Entorno->TString->GetString(valor).c_str();
		n = Entorno->TString->GetString(valor).length();
	} else {
		if(valor > 0xFF)
			throw(string("Desbordamiento (RETLW K, se espera K < 256)"));
		n = 1;
		c = valor & 0xFF;
	}
	if(Entorno->GetPaso() == 1) {
		Entorno->m->p += n;
		return;
	}
	for(int i=0; i<n; i++)
		Entorno->m->SetROM(0x3400 | (unsigned char)(s[i]));
}

unsigned long emitir_division(unsigned long a, unsigned long b)
{
	if(!b)
		throw(string("division sobre cero"));
	return a/b;
}

/* Operadores miembros de la clase struct_arg */
void struct_arg::operator=(unsigned long _num) { val.num=_num;   }
void struct_arg::operator=(simbolo *_sbl)      { val.sbl=_sbl;   }
struct_arg::operator unsigned long()           { return val.num; }
struct_arg::operator simbolo*()                { return val.sbl; }
